package com.grape.utils;

import com.grape.error.BusinessException;
import lombok.AllArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 类描述：锁工具类
 *
 * @author  putao
 * @email   2628279194@qq.com
 */
@Component
@AllArgsConstructor
public class LockUtil {

    private final RedissonClient redissonClient;

    /**
     * 获取锁并执行方法
     * @param key           前缀
     * @param waitTime      等待时间
     * @param unit          等待时间单位
     * @param supplier      消费型接口
     * @return              方法返回值
     * @param <T>           泛型
     * @throws Throwable    异常
     */
    public <T> T executeWithLockThrows(String key, int waitTime, TimeUnit unit, SupplierThrow<T> supplier) throws Throwable {
        RLock lock = redissonClient.getLock(key);
        boolean lockSuccess = lock.tryLock(waitTime, unit);
        if (!lockSuccess) {
            throw new BusinessException("太快了，请稍后再试...");
        }
        // 判断获取到锁是否为当前线程
        try {
            if (lock.isHeldByCurrentThread()) {
                // 执行锁内的代码逻辑
                return supplier.get();
            }
            throw new BusinessException("执行错误，请联系管理员...");
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 切面执行方法可能会抛异常，jdk提供消费型接口不支持抛出异常，所以定制个
     * @param <T>
     */
    @FunctionalInterface
    public interface SupplierThrow<T> {

        /**
         * Gets a result.
         *
         * @return a result
         */
        T get() throws Throwable;
    }

}
